package cn.jly.spring.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 概念
 * 1. 连接点
 * 类里面哪些方法可以被增强，这些方法叫做连接点
 * <p>
 * 2. 切入点
 * 实际被增强的方法，成为切入点
 * <p>
 * 3. 通知（增强）
 * a. 实际增强的逻辑部分成为通知
 * b. 通知有多种类型：
 * - 前置通知
 * - 后置通知
 * - 异常通知
 * - 最终通知
 * - 环绕通知
 * <p>
 * 4. 切面 - 是动作
 * 把各种通知应用到切入点的过程
 * <p>
 * 5. 切入点表达式
 * 作用：指明对哪个类的哪个方法进行增强
 * 语法：execution([权限修饰符][返回类型][类全路径][类名]([参数列表]))
 * <p>
 * 6. 多个切面对同一个类的方法进行增强，设置优先级
 * 利用注解@Order(数值)，数值越小优先级越高
 *
 * @author lanyangji
 * @description 切面
 * @date 2020/11/10 下午 4:00
 */
@Component
@Aspect
@Order(1)
public class LoggerAspect {

    /**
     * 切入点表达式
     */
    @Pointcut("execution(* cn.jly.spring.aop.dao.CustomerDao.*(..))")
    public void expression() {
    }

    /**
     * 前置通知
     */
    @Before("expression()")
    public void beforeExec(JoinPoint joinPoint) {
        System.out.println("-------------------LoggerAspect.beforeExec - 前置通知 -------------------");
        System.out.println("方法名：" + joinPoint.getSignature().getName());
        System.out.println("所在类：" + joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("方法入参：" + Arrays.toString(joinPoint.getArgs()));
        System.out.println(joinPoint.getSignature().getModifiers());
    }

    /**
     * 最终通知 - 不管有没有发生异常，都会执行
     *
     * @param joinPoint
     */
    @After("expression()")
    public void afterExec(JoinPoint joinPoint) {
        System.out.println("最终通知");
    }

    /**
     * 异常通知
     *
     * @param ex
     */
    @AfterThrowing(value = "expression()", throwing = "ex")
    public void afterThrowing(Exception ex) {
        System.out.println("异常通知，异常信息为 = " + ex.getMessage());
    }

    /**
     * 正常返回通知，也可叫后置通知
     *
     * @param res
     */
    @AfterReturning(value = "expression()", returning = "res")
    public void afterReturning(Object res) {
        System.out.println("正常返回通知，结果为 = " + res);
    }

    /**
     * 环绕通知
     */
    @Around("expression()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("--------------环绕之前------------------");

        // 正常执行目标方法
        final Object res = proceedingJoinPoint.proceed();
        System.out.println("目标方法执行结果为 = " + res);

        System.out.println("--------------环绕之后------------------");

        // 方法执行结果返回
        return res;
    }
}
